home *** CD-ROM | disk | FTP | other *** search
/ PCGUIA 127 / PC Guia 127.iso / Software / Produtividade / OpenOffice.org 2.0.1 / openofficeorg3.cab / trace.py < prev    next >
Text File  |  2005-11-19  |  25KB  |  693 lines

  1. #!/usr/bin/env python
  2.  
  3. # portions copyright 2001, Autonomous Zones Industries, Inc., all rights...
  4. # err...  reserved and offered to the public under the terms of the
  5. # Python 2.2 license.
  6. # Author: Zooko O'Whielacronx
  7. # http://zooko.com/
  8. # mailto:zooko@zooko.com
  9. #
  10. # Copyright 2000, Mojam Media, Inc., all rights reserved.
  11. # Author: Skip Montanaro
  12. #
  13. # Copyright 1999, Bioreason, Inc., all rights reserved.
  14. # Author: Andrew Dalke
  15. #
  16. # Copyright 1995-1997, Automatrix, Inc., all rights reserved.
  17. # Author: Skip Montanaro
  18. #
  19. # Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved.
  20. #
  21. #
  22. # Permission to use, copy, modify, and distribute this Python software and
  23. # its associated documentation for any purpose without fee is hereby
  24. # granted, provided that the above copyright notice appears in all copies,
  25. # and that both that copyright notice and this permission notice appear in
  26. # supporting documentation, and that the name of neither Automatrix,
  27. # Bioreason or Mojam Media be used in advertising or publicity pertaining to
  28. # distribution of the software without specific, written prior permission.
  29. #
  30. """program/module to trace Python program or function execution
  31.  
  32. Sample use, command line:
  33.   trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs
  34.   trace.py -t --ignore-dir '$prefix' spam.py eggs
  35.  
  36. Sample use, programmatically
  37.    # create a Trace object, telling it what to ignore, and whether to
  38.    # do tracing or line-counting or both.
  39.    trace = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0,
  40.                        count=1)
  41.    # run the new command using the given trace
  42.    trace.run(coverage.globaltrace, 'main()')
  43.    # make a report, telling it where you want output
  44.    r = trace.results()
  45.    r.write_results(show_missing=True)
  46. """
  47.  
  48. import linecache
  49. import marshal
  50. import os
  51. import re
  52. import sys
  53. import threading
  54. import token
  55. import tokenize
  56. import types
  57.  
  58. try:
  59.     import cPickle
  60.     pickle = cPickle
  61. except ImportError:
  62.     import pickle
  63.  
  64. def usage(outfile):
  65.     outfile.write("""Usage: %s [OPTIONS] <file> [ARGS]
  66.  
  67. Meta-options:
  68. --help                Display this help then exit.
  69. --version             Output version information then exit.
  70.  
  71. Otherwise, exactly one of the following three options must be given:
  72. -t, --trace           Print each line to sys.stdout before it is executed.
  73. -c, --count           Count the number of times each line is executed
  74.                       and write the counts to <module>.cover for each
  75.                       module executed, in the module's directory.
  76.                       See also `--coverdir', `--file', `--no-report' below.
  77. -l, --listfuncs       Keep track of which functions are executed at least
  78.                       once and write the results to sys.stdout after the
  79.                       program exits.
  80. -r, --report          Generate a report from a counts file; do not execute
  81.                       any code.  `--file' must specify the results file to
  82.                       read, which must have been created in a previous run
  83.                       with `--count --file=FILE'.
  84.  
  85. Modifiers:
  86. -f, --file=<file>     File to accumulate counts over several runs.
  87. -R, --no-report       Do not generate the coverage report files.
  88.                       Useful if you want to accumulate over several runs.
  89. -C, --coverdir=<dir>  Directory where the report files.  The coverage
  90.                       report for <package>.<module> is written to file
  91.                       <dir>/<package>/<module>.cover.
  92. -m, --missing         Annotate executable lines that were not executed
  93.                       with '>>>>>> '.
  94. -s, --summary         Write a brief summary on stdout for each file.
  95.                       (Can only be used with --count or --report.)
  96.  
  97. Filters, may be repeated multiple times:
  98. --ignore-module=<mod> Ignore the given module and its submodules
  99.                       (if it is a package).
  100. --ignore-dir=<dir>    Ignore files in the given directory (multiple
  101.                       directories can be joined by os.pathsep).
  102. """ % sys.argv[0])
  103.  
  104. PRAGMA_NOCOVER = "#pragma NO COVER"
  105.  
  106. # Simple rx to find lines with no code.
  107. rx_blank = re.compile(r'^\s*(#.*)?$')
  108.  
  109. class Ignore:
  110.     def __init__(self, modules = None, dirs = None):
  111.         self._mods = modules or []
  112.         self._dirs = dirs or []
  113.  
  114.         self._dirs = map(os.path.normpath, self._dirs)
  115.         self._ignore = { '<string>': 1 }
  116.  
  117.     def names(self, filename, modulename):
  118.         if self._ignore.has_key(modulename):
  119.             return self._ignore[modulename]
  120.  
  121.         # haven't seen this one before, so see if the module name is
  122.         # on the ignore list.  Need to take some care since ignoring
  123.         # "cmp" musn't mean ignoring "cmpcache" but ignoring
  124.         # "Spam" must also mean ignoring "Spam.Eggs".
  125.         for mod in self._mods:
  126.             if mod == modulename:  # Identical names, so ignore
  127.                 self._ignore[modulename] = 1
  128.                 return 1
  129.             # check if the module is a proper submodule of something on
  130.             # the ignore list
  131.             n = len(mod)
  132.             # (will not overflow since if the first n characters are the
  133.             # same and the name has not already occured, then the size
  134.             # of "name" is greater than that of "mod")
  135.             if mod == modulename[:n] and modulename[n] == '.':
  136.                 self._ignore[modulename] = 1
  137.                 return 1
  138.  
  139.         # Now check that __file__ isn't in one of the directories
  140.         if filename is None:
  141.             # must be a built-in, so we must ignore
  142.             self._ignore[modulename] = 1
  143.             return 1
  144.  
  145.         # Ignore a file when it contains one of the ignorable paths
  146.         for d in self._dirs:
  147.             # The '+ os.sep' is to ensure that d is a parent directory,
  148.             # as compared to cases like:
  149.             #  d = "/usr/local"
  150.             #  filename = "/usr/local.py"
  151.             # or
  152.             #  d = "/usr/local.py"
  153.             #  filename = "/usr/local.py"
  154.             if filename.startswith(d + os.sep):
  155.                 self._ignore[modulename] = 1
  156.                 return 1
  157.  
  158.         # Tried the different ways, so we don't ignore this module
  159.         self._ignore[modulename] = 0
  160.         return 0
  161.  
  162. def modname(path):
  163.     """Return a plausible module name for the patch."""
  164.  
  165.     base = os.path.basename(path)
  166.     filename, ext = os.path.splitext(base)
  167.     return filename
  168.  
  169. def fullmodname(path):
  170.     """Return a plausible module name for the path."""
  171.  
  172.     # If the file 'path' is part of a package, then the filename isn't
  173.     # enough to uniquely identify it.  Try to do the right thing by
  174.     # looking in sys.path for the longest matching prefix.  We'll
  175.     # assume that the rest is the package name.
  176.  
  177.     longest = ""
  178.     for dir in sys.path:
  179.         if path.startswith(dir) and path[len(dir)] == os.path.sep:
  180.             if len(dir) > len(longest):
  181.                 longest = dir
  182.  
  183.     if longest:
  184.         base = path[len(longest) + 1:]
  185.     else:
  186.         base = path
  187.     base = base.replace(os.sep, ".")
  188.     if os.altsep:
  189.         base = base.replace(os.altsep, ".")
  190.     filename, ext = os.path.splitext(base)
  191.     return filename
  192.  
  193. class CoverageResults:
  194.     def __init__(self, counts=None, calledfuncs=None, infile=None,
  195.                  outfile=None):
  196.         self.counts = counts
  197.         if self.counts is None:
  198.             self.counts = {}
  199.         self.counter = self.counts.copy() # map (filename, lineno) to count
  200.         self.calledfuncs = calledfuncs
  201.         if self.calledfuncs is None:
  202.             self.calledfuncs = {}
  203.         self.calledfuncs = self.calledfuncs.copy()
  204.         self.infile = infile
  205.         self.outfile = outfile
  206.         if self.infile:
  207.             # Try to merge existing counts file.
  208.             try:
  209.                 counts, calledfuncs = pickle.load(open(self.infile, 'rb'))
  210.                 self.update(self.__class__(counts, calledfuncs))
  211.             except (IOError, EOFError, ValueError), err:
  212.                 print >> sys.stderr, ("Skipping counts file %r: %s"
  213.                                       % (self.infile, err))
  214.  
  215.     def update(self, other):
  216.         """Merge in the data from another CoverageResults"""
  217.         counts = self.counts
  218.         calledfuncs = self.calledfuncs
  219.         other_counts = other.counts
  220.         other_calledfuncs = other.calledfuncs
  221.  
  222.         for key in other_counts.keys():
  223.             counts[key] = counts.get(key, 0) + other_counts[key]
  224.  
  225.         for key in other_calledfuncs.keys():
  226.             calledfuncs[key] = 1
  227.  
  228.     def write_results(self, show_missing=True, summary=False, coverdir=None):
  229.         """
  230.         @param coverdir
  231.         """
  232.         for filename, modulename, funcname in self.calledfuncs.keys():
  233.             print ("filename: %s, modulename: %s, funcname: %s"
  234.                    % (filename, modulename, funcname))
  235.  
  236.         # turn the counts data ("(filename, lineno) = count") into something
  237.         # accessible on a per-file basis
  238.         per_file = {}
  239.         for filename, lineno in self.counts.keys():
  240.             lines_hit = per_file[filename] = per_file.get(filename, {})
  241.             lines_hit[lineno] = self.counts[(filename, lineno)]
  242.  
  243.         # accumulate summary info, if needed
  244.         sums = {}
  245.  
  246.         for filename, count in per_file.iteritems():
  247.             # skip some "files" we don't care about...
  248.             if filename == "<string>":
  249.                 continue
  250.  
  251.             if filename.endswith(".pyc") or filename.endswith(".pyo"):
  252.                 filename = filename[:-1]
  253.  
  254.             if coverdir is None:
  255.                 dir = os.path.dirname(os.path.abspath(filename))
  256.                 modulename = modname(filename)
  257.             else:
  258.                 dir = coverdir
  259.                 if not os.path.exists(dir):
  260.                     os.makedirs(dir)
  261.                 modulename = fullmodname(filename)
  262.  
  263.             # If desired, get a list of the line numbers which represent
  264.             # executable content (returned as a dict for better lookup speed)
  265.             if show_missing:
  266.                 lnotab = find_executable_linenos(filename)
  267.             else:
  268.                 lnotab = {}
  269.  
  270.             source = linecache.getlines(filename)
  271.             coverpath = os.path.join(dir, modulename + ".cover")
  272.             n_hits, n_lines = self.write_results_file(coverpath, source,
  273.                                                       lnotab, count)
  274.  
  275.             if summary and n_lines:
  276.                 percent = int(100 * n_hits / n_lines)
  277.                 sums[modulename] = n_lines, percent, modulename, filename
  278.  
  279.         if summary and sums:
  280.             mods = sums.keys()
  281.             mods.sort()
  282.             print "lines   cov%   module   (path)"
  283.             for m in mods:
  284.                 n_lines, percent, modulename, filename = sums[m]
  285.                 print "%5d   %3d%%   %s   (%s)" % sums[m]
  286.  
  287.         if self.outfile:
  288.             # try and store counts and module info into self.outfile
  289.             try:
  290.                 pickle.dump((self.counts, self.calledfuncs),
  291.                             open(self.outfile, 'wb'), 1)
  292.             except IOError, err:
  293.                 print >> sys.stderr, "Can't save counts files because %s" % err
  294.  
  295.     def write_results_file(self, path, lines, lnotab, lines_hit):
  296.         """Return a coverage results file in path."""
  297.  
  298.         try:
  299.             outfile = open(path, "w")
  300.         except IOError, err:
  301.             print >> sys.stderr, ("trace: Could not open %r for writing: %s"
  302.                                   "- skipping" % (path, err))
  303.             return 0, 0
  304.  
  305.         n_lines = 0
  306.         n_hits = 0
  307.         for i, line in enumerate(lines):
  308.             lineno = i + 1
  309.             # do the blank/comment match to try to mark more lines
  310.             # (help the reader find stuff that hasn't been covered)
  311.             if lineno in lines_hit:
  312.                 outfile.write("%5d: " % lines_hit[lineno])
  313.                 n_hits += 1
  314.                 n_lines += 1
  315.             elif rx_blank.match(line):
  316.                 outfile.write("       ")
  317.             else:
  318.                 # lines preceded by no marks weren't hit
  319.                 # Highlight them if so indicated, unless the line contains
  320.                 # #pragma: NO COVER
  321.                 if lineno in lnotab and not PRAGMA_NOCOVER in lines[i]:
  322.                     outfile.write(">>>>>> ")
  323.                     n_lines += 1
  324.                 else:
  325.                     outfile.write("       ")
  326.             outfile.write(lines[i].expandtabs(8))
  327.         outfile.close()
  328.  
  329.         return n_hits, n_lines
  330.  
  331. def find_lines_from_code(code, strs):
  332.     """Return dict where keys are lines in the line number table."""
  333.     linenos = {}
  334.  
  335.     line_increments = [ord(c) for c in code.co_lnotab[1::2]]
  336.     table_length = len(line_increments)
  337.     docstring = False
  338.  
  339.     lineno = code.co_firstlineno
  340.     for li in line_increments:
  341.         lineno += li
  342.         if lineno not in strs:
  343.             linenos[lineno] = 1
  344.  
  345.     return linenos
  346.  
  347. def find_lines(code, strs):
  348.     """Return lineno dict for all code objects reachable from code."""
  349.     # get all of the lineno information from the code of this scope level
  350.     linenos = find_lines_from_code(code, strs)
  351.  
  352.     # and check the constants for references to other code objects
  353.     for c in code.co_consts:
  354.         if isinstance(c, types.CodeType):
  355.             # find another code object, so recurse into it
  356.             linenos.update(find_lines(c, strs))
  357.     return linenos
  358.  
  359. def find_strings(filename):
  360.     """Return a dict of possible docstring positions.
  361.  
  362.     The dict maps line numbers to strings.  There is an entry for
  363.     line that contains only a string or a part of a triple-quoted
  364.     string.
  365.     """
  366.     d = {}
  367.     # If the first token is a string, then it's the module docstring.
  368.     # Add this special case so that the test in the loop passes.
  369.     prev_ttype = token.INDENT
  370.     f = open(filename)
  371.     for ttype, tstr, start, end, line in tokenize.generate_tokens(f.readline):
  372.         if ttype == token.STRING:
  373.             if prev_ttype == token.INDENT:
  374.                 sline, scol = start
  375.                 eline, ecol = end
  376.                 for i in range(sline, eline + 1):
  377.                     d[i] = 1
  378.         prev_ttype = ttype
  379.     f.close()
  380.     return d
  381.  
  382. def find_executable_linenos(filename):
  383.     """Return dict where keys are line numbers in the line number table."""
  384.     assert filename.endswith('.py')
  385.     try:
  386.         prog = open(filename, "rU").read()
  387.     except IOError, err:
  388.         print >> sys.stderr, ("Not printing coverage data for %r: %s"
  389.                               % (filename, err))
  390.         return {}
  391.     code = compile(prog, filename, "exec")
  392.     strs = find_strings(filename)
  393.     return find_lines(code, strs)
  394.  
  395. class Trace:
  396.     def __init__(self, count=1, trace=1, countfuncs=0, ignoremods=(),
  397.                  ignoredirs=(), infile=None, outfile=None):
  398.         """
  399.         @param count true iff it should count number of times each
  400.                      line is executed
  401.         @param trace true iff it should print out each line that is
  402.                      being counted
  403.         @param countfuncs true iff it should just output a list of
  404.                      (filename, modulename, funcname,) for functions
  405.                      that were called at least once;  This overrides
  406.                      `count' and `trace'
  407.         @param ignoremods a list of the names of modules to ignore
  408.         @param ignoredirs a list of the names of directories to ignore
  409.                      all of the (recursive) contents of
  410.         @param infile file from which to read stored counts to be
  411.                      added into the results
  412.         @param outfile file in which to write the results
  413.         """
  414.         self.infile = infile
  415.         self.outfile = outfile
  416.         self.ignore = Ignore(ignoremods, ignoredirs)
  417.         self.counts = {}   # keys are (filename, linenumber)
  418.         self.blabbed = {} # for debugging
  419.         self.pathtobasename = {} # for memoizing os.path.basename
  420.         self.donothing = 0
  421.         self.trace = trace
  422.         self._calledfuncs = {}
  423.         if countfuncs:
  424.             self.globaltrace = self.globaltrace_countfuncs
  425.         elif trace and count:
  426.             self.globaltrace = self.globaltrace_lt
  427.             self.localtrace = self.localtrace_trace_and_count
  428.         elif trace:
  429.             self.globaltrace = self.globaltrace_lt
  430.             self.localtrace = self.localtrace_trace
  431.         elif count:
  432.             self.globaltrace = self.globaltrace_lt
  433.             self.localtrace = self.localtrace_count
  434.         else:
  435.             # Ahem -- do nothing?  Okay.
  436.             self.donothing = 1
  437.  
  438.     def run(self, cmd):
  439.         import __main__
  440.         dict = __main__.__dict__
  441.         if not self.donothing:
  442.             sys.settrace(self.globaltrace)
  443.             threading.settrace(self.globaltrace)
  444.         try:
  445.             exec cmd in dict, dict
  446.         finally:
  447.             if not self.donothing:
  448.                 sys.settrace(None)
  449.                 threading.settrace(None)
  450.  
  451.     def runctx(self, cmd, globals=None, locals=None):
  452.         if globals is None: globals = {}
  453.         if locals is None: locals = {}
  454.         if not self.donothing:
  455.             sys.settrace(self.globaltrace)
  456.             threading.settrace(self.globaltrace)
  457.         try:
  458.             exec cmd in globals, locals
  459.         finally:
  460.             if not self.donothing:
  461.                 sys.settrace(None)
  462.                 threading.settrace(None)
  463.  
  464.     def runfunc(self, func, *args, **kw):
  465.         result = None
  466.         if not self.donothing:
  467.             sys.settrace(self.globaltrace)
  468.         try:
  469.             result = func(*args, **kw)
  470.         finally:
  471.             if not self.donothing:
  472.                 sys.settrace(None)
  473.         return result
  474.  
  475.     def globaltrace_countfuncs(self, frame, why, arg):
  476.         """Handler for call events.
  477.  
  478.         Adds (filename, modulename, funcname) to the self._calledfuncs dict.
  479.         """
  480.         if why == 'call':
  481.             code = frame.f_code
  482.             filename = code.co_filename
  483.             funcname = code.co_name
  484.             if filename:
  485.                 modulename = modname(filename)
  486.             else:
  487.                 modulename = None
  488.             self._calledfuncs[(filename, modulename, funcname)] = 1
  489.  
  490.     def globaltrace_lt(self, frame, why, arg):
  491.         """Handler for call events.
  492.  
  493.         If the code block being entered is to be ignored, returns `None',
  494.         else returns self.localtrace.
  495.         """
  496.         if why == 'call':
  497.             code = frame.f_code
  498.             filename = code.co_filename
  499.             if filename:
  500.                 # XXX modname() doesn't work right for packages, so
  501.                 # the ignore support won't work right for packages
  502.                 modulename = modname(filename)
  503.                 if modulename is not None:
  504.                     ignore_it = self.ignore.names(filename, modulename)
  505.                     if not ignore_it:
  506.                         if self.trace:
  507.                             print (" --- modulename: %s, funcname: %s"
  508.                                    % (modulename, code.co_name))
  509.                         return self.localtrace
  510.             else:
  511.                 return None
  512.  
  513.     def localtrace_trace_and_count(self, frame, why, arg):
  514.         if why == "line":
  515.             # record the file name and line number of every trace
  516.             filename = frame.f_code.co_filename
  517.             lineno = frame.f_lineno
  518.             key = filename, lineno
  519.             self.counts[key] = self.counts.get(key, 0) + 1
  520.  
  521.             bname = os.path.basename(filename)
  522.             print "%s(%d): %s" % (bname, lineno,
  523.                                   linecache.getline(filename, lineno)),
  524.         return self.localtrace
  525.  
  526.     def localtrace_trace(self, frame, why, arg):
  527.         if why == "line":
  528.             # record the file name and line number of every trace
  529.             filename = frame.f_code.co_filename
  530.             lineno = frame.f_lineno
  531.  
  532.             bname = os.path.basename(filename)
  533.             print "%s(%d): %s" % (bname, lineno,
  534.                                   linecache.getline(filename, lineno)),
  535.         return self.localtrace
  536.  
  537.     def localtrace_count(self, frame, why, arg):
  538.         if why == "line":
  539.             filename = frame.f_code.co_filename
  540.             lineno = frame.f_lineno
  541.             key = filename, lineno
  542.             self.counts[key] = self.counts.get(key, 0) + 1
  543.         return self.localtrace
  544.  
  545.     def results(self):
  546.         return CoverageResults(self.counts, infile=self.infile,
  547.                                outfile=self.outfile,
  548.                                calledfuncs=self._calledfuncs)
  549.  
  550. def _err_exit(msg):
  551.     sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
  552.     sys.exit(1)
  553.  
  554. def main(argv=None):
  555.     import getopt
  556.  
  557.     if argv is None:
  558.         argv = sys.argv
  559.     try:
  560.         opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:l",
  561.                                         ["help", "version", "trace", "count",
  562.                                          "report", "no-report", "summary",
  563.                                          "file=", "missing",
  564.                                          "ignore-module=", "ignore-dir=",
  565.                                          "coverdir=", "listfuncs",])
  566.  
  567.     except getopt.error, msg:
  568.         sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
  569.         sys.stderr.write("Try `%s --help' for more information\n"
  570.                          % sys.argv[0])
  571.         sys.exit(1)
  572.  
  573.     trace = 0
  574.     count = 0
  575.     report = 0
  576.     no_report = 0
  577.     counts_file = None
  578.     missing = 0
  579.     ignore_modules = []
  580.     ignore_dirs = []
  581.     coverdir = None
  582.     summary = 0
  583.     listfuncs = False
  584.  
  585.     for opt, val in opts:
  586.         if opt == "--help":
  587.             usage(sys.stdout)
  588.             sys.exit(0)
  589.  
  590.         if opt == "--version":
  591.             sys.stdout.write("trace 2.0\n")
  592.             sys.exit(0)
  593.  
  594.         if opt == "-l" or opt == "--listfuncs":
  595.             listfuncs = True
  596.             continue
  597.  
  598.         if opt == "-t" or opt == "--trace":
  599.             trace = 1
  600.             continue
  601.  
  602.         if opt == "-c" or opt == "--count":
  603.             count = 1
  604.             continue
  605.  
  606.         if opt == "-r" or opt == "--report":
  607.             report = 1
  608.             continue
  609.  
  610.         if opt == "-R" or opt == "--no-report":
  611.             no_report = 1
  612.             continue
  613.  
  614.         if opt == "-f" or opt == "--file":
  615.             counts_file = val
  616.             continue
  617.  
  618.         if opt == "-m" or opt == "--missing":
  619.             missing = 1
  620.             continue
  621.  
  622.         if opt == "-C" or opt == "--coverdir":
  623.             coverdir = val
  624.             continue
  625.  
  626.         if opt == "-s" or opt == "--summary":
  627.             summary = 1
  628.             continue
  629.  
  630.         if opt == "--ignore-module":
  631.             ignore_modules.append(val)
  632.             continue
  633.  
  634.         if opt == "--ignore-dir":
  635.             for s in val.split(os.pathsep):
  636.                 s = os.path.expandvars(s)
  637.                 # should I also call expanduser? (after all, could use $HOME)
  638.  
  639.                 s = s.replace("$prefix",
  640.                               os.path.join(sys.prefix, "lib",
  641.                                            "python" + sys.version[:3]))
  642.                 s = s.replace("$exec_prefix",
  643.                               os.path.join(sys.exec_prefix, "lib",
  644.                                            "python" + sys.version[:3]))
  645.                 s = os.path.normpath(s)
  646.                 ignore_dirs.append(s)
  647.             continue
  648.  
  649.         assert 0, "Should never get here"
  650.  
  651.     if listfuncs and (count or trace):
  652.         _err_exit("cannot specify both --listfuncs and (--trace or --count)")
  653.  
  654.     if not count and not trace and not report and not listfuncs:
  655.         _err_exit("must specify one of --trace, --count, --report or "
  656.                   "--listfuncs")
  657.  
  658.     if report and no_report:
  659.         _err_exit("cannot specify both --report and --no-report")
  660.  
  661.     if report and not counts_file:
  662.         _err_exit("--report requires a --file")
  663.  
  664.     if no_report and len(prog_argv) == 0:
  665.         _err_exit("missing name of file to run")
  666.  
  667.     # everything is ready
  668.     if report:
  669.         results = CoverageResults(infile=counts_file, outfile=counts_file)
  670.         results.write_results(missing, summary=summary, coverdir=coverdir)
  671.     else:
  672.         sys.argv = prog_argv
  673.         progname = prog_argv[0]
  674.         sys.path[0] = os.path.split(progname)[0]
  675.  
  676.         t = Trace(count, trace, countfuncs=listfuncs,
  677.                   ignoremods=ignore_modules, ignoredirs=ignore_dirs,
  678.                   infile=counts_file, outfile=counts_file)
  679.         try:
  680.             t.run('execfile(' + `progname` + ')')
  681.         except IOError, err:
  682.             _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err))
  683.         except SystemExit:
  684.             pass
  685.  
  686.         results = t.results()
  687.  
  688.         if not no_report:
  689.             results.write_results(missing, summary=summary, coverdir=coverdir)
  690.  
  691. if __name__=='__main__':
  692.     main()
  693.